package no.ntnu.item.ttm4120.dhcp;

import java.net.InetAddress;
import java.util.Timer;
import java.util.TreeMap;

import javax.swing.plaf.SliderUI;

import gmi.MembershipListener;
import gmi.ServerSideProxy;
import gmi.View;
import gmi.protocols.Anycast;
import gmi.protocols.Multicast;
import hello.HelloServer;
import no.ntnu.item.ttm4120.dhcp.data.Address;
import no.ntnu.item.ttm4120.dhcp.data.AddressPoolEntity;
import no.ntnu.item.ttm4120.dhcp.exception.AddressPoolException;

public class AddressServer implements AddressServerEGMI, AddressServerIGMI, MembershipListener{

	private static final long serialVersionUID = 8401404452281773348L;

    private String groupname="group05";
    private String serverAddress = "localhost";
    private ServerSideProxy serverSideProxy;
    private AddressServerIGMI serverIGMI;
    private AddressPool addressPool = null;
    private Timer leaseTimer;
    private int serverPermPos = -1;
    
	public AddressServer(String name, int port) {
    	//Getting ServerSideProxy Object to communicate with Spread Daemon.
    	serverSideProxy = new ServerSideProxy(this, port, name, serverAddress);
    	//Join server group.
    	serverSideProxy.join(groupname);
    	//internalHello to reference dynamic proxy object for IGMI invocation.
    	serverIGMI = (AddressServerIGMI) serverSideProxy.getInternalStub(AddressServerIGMI.class);
    	
    	LeaseHandler leaseHandler = new LeaseHandler(this);
    	new Thread(leaseHandler).start();
	}
	
	@Override
	public long getLeaseLength(InetAddress inetAddress) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	@Anycast
	public AddressPoolEntity getNewAddress(String client, long lease) throws AddressPoolException {
		AddressPoolEntity add = getAddressPool().getNewAddress(client, lease);
		if(add == null){
			System.err.println("Nuuuuuuuuuuuuullllllllll" );
		}
		serverIGMI.reserverAddress(add, serverSideProxy.getIdentifier());
//		System.err.println("LastCheck: " + add);
		return add;
	}

	@Override
	@Multicast
	public void refreshLease(InetAddress inetAddress, long lease) {
		AddressPoolEntity ape = getAddressPool().getPool().get(inetAddress.getHostAddress());
		System.out.println("Refreshing leaseTime for: " + ape);
		ape.setLeaseTimeEnd(lease + System.currentTimeMillis());
	}

	@Override
	public void releaseAddress(AddressPoolEntity address, String issuerServer) {
		if(!issuerServer.equalsIgnoreCase(serverSideProxy.getIdentifier())){
			System.out.println("Releasing message has received from: " + issuerServer + ", Following lease is expired :");
			System.out.println(address);
			getAddressPool().updatePool(address, issuerServer);
		}
		
	}

	@Override
	public void syncAddressPool(TreeMap<String, AddressPoolEntity> pool, String issuerServer) {
		System.out.println("Syncing AddressPool for server: " + serverSideProxy.getIdentifier());
		if(!issuerServer.equalsIgnoreCase(serverSideProxy.getIdentifier())){
			System.out.println("Syncing AddressPool for server: " + serverSideProxy.getIdentifier());
			System.out.println("AddressPool Assigned Addresses: " );
			System.out.println(pool);
			getAddressPool().setPool(pool);
		}
	}
	
	@Override
	public void reserverAddress(AddressPoolEntity ape, String issuerServer) {
		if(!issuerServer.equalsIgnoreCase(serverSideProxy.getIdentifier())){
			getAddressPool().updatePool(ape, issuerServer);
		}else{
//			System.err.println("Myself-00000000000000000000000");
		}
	}

	@Override
	public void ViewChange(View view) {
		try {
			if(getAddressPool() == null){
				System.out.println("Initializing AddressPool for server: " + serverSideProxy.getIdentifier() +
						" with Position: " + getMemberPosition(view, serverSideProxy.getIdentifier()) );
				setAddressPool(new AddressPool(getMemberPosition(view, serverSideProxy.getIdentifier())));
			}
			System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++");
			System.out.println(view);
				System.out.println(getMemberPosition(view, serverSideProxy.getIdentifier()));
			System.out.println(serverSideProxy.getIdentifier());
			System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++");
			if(view.getView().size() < 2){
				if (!addressPool.shouldTakeOver()){
					System.out.println("It seems that I am the only server running");
					System.out.println("Crashed happened in the ObjectGroup, Taking Over addresspool ...");
					addressPool.setTakeOver(true);
				}
			}
			if(view.getView().size() > 1){
				if (addressPool.shouldTakeOver()){
					System.out.println("It seems that I am NOT the only server running");
					System.out.println("Server recovered in the ObjectGroup, Syncing Pool and Returning to normal operation ...");
					addressPool.setTakeOver(false);
					serverIGMI.syncAddressPool(getAddressPool().getPool(), serverSideProxy.getIdentifier());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void reclaimExpiredLease() {
		// TODO Auto-generated method stub
		
	}

	public void leaseExpired(AddressPoolEntity ape){
		// Maybe set ape to null be a better solution
		// Some sort of garbage collecting mechanism is needed.
//		System.out.println("Lease expired for Address: " + ape);
		getAddressPool().getPool().get(ape.getAddress().getHostAddress()).setAssigned(false);
		serverIGMI.releaseAddress(ape, serverSideProxy.getIdentifier());
	}
	 public static void main(String[] arg) {
	       String connName = null;
	       int port = 0;
	       try {
	           for (int i = 0 ; i < arg.length ; i += 2) {
	        	   if (arg[i].equals("-c")) {
	        		   connName = arg[i+1];
	        	   } else if (arg[i].equals("-p")) {
	        		   port = Integer.parseInt(arg[i+1]);
	        	   } else {
	        		   usage();
	        	   }
	           }
	       }
	       catch (Exception e) {
	    	   usage();
	       }
	       new AddressServer(connName, port);   
	       System.out.println("AddressServer is up and running");
	       //thread sleep to avoid cpu utilization when they are waiting for any messages
	       while (true) {
	    	   try {
	    		   Thread.sleep(100);
	    	   } catch (InterruptedException e) {
	    		   e.printStackTrace();
	    	   }
	       }
	    }
	 
	    private static void usage() {
	    	System.out.println("Usage Server :: server -c <srvname> -p <port>");
	    	System.exit(1);
	    }

		private void setAddressPool(AddressPool addressPool) {
			this.addressPool = addressPool;
		}

		public AddressPool getAddressPool() {
			return addressPool;
		}
		
	    /**
	     * 
	     * @param server = ServerSideProxy.getIdentif
	     * @return
	     */
	    public int getMemberPosition(View view, String server){
	    	if(serverPermPos == -1){
		    	int position = -1;
		    	for (int i = 0; i < view.getView().size(); i++) {
					if(view.memberHasPosition(i, server)){
						position = i;
						serverPermPos = position;
						return position;
					}
				}
	    	}
	    	return serverPermPos;
	    }

	    public int getServerPos(){
	    	return serverPermPos;
	    }
		
}
